package Server;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.nio.charset.Charset;
import java.util.SortedSet;

/**
 * Thread to handle UDP client requests.
 * 
 * @author the_james_marq
 * @version 11 October 13
 */
public class UDPSpellingServerThread extends Thread {
	/**
	 * Socket on which to communicate.
	 */
	protected DatagramSocket my_socket = null;
	/**
	 * Word list object to search.
	 */
	private WordList my_word_list;
	
	/**
	 * Constructor for an unnamed thread.
	 * 
	 * @param port Port for this server to listen and transmit on.
	 * @param file Filename of dictionary.
	 * @throws IOException  
	 */
	public UDPSpellingServerThread(String port, String file) {
		this("UDPSpellingServerThread", port, file);
	}
	
	/**
	 * Constructor for UDPSpellingServerThread
	 * 
	 * @param name Name of this thread.
	 * @param port Port for this server to listen and transmit on.
	 * @param file Filename of dictionary.
	 */
	public UDPSpellingServerThread(String name, String port, String file){
		super(name);

		try {
			my_word_list = new WordList(file);
		} catch (IOException e) {
			Log.err("Unable to open file " + file);
		}
		
		try {
			my_socket = new DatagramSocket(Integer.parseInt(port));
			my_socket.setReuseAddress(true);
		} catch (SocketException e) {
			Log.err("Unable to open socket on port " + port);
		}
		
	}

	/**
	 * Receive requests and transmit respones.
	 */
	public void run() {

		while(true){
			byte[] buf = new byte[512];
			byte[] ret_bytes = null;
			ByteArrayOutputStream byte_str = new ByteArrayOutputStream();

			Charset c = Charset.forName("US-ASCII");

			DatagramPacket packet = new DatagramPacket(buf, buf.length);
			try {
				my_socket.receive(packet);
				Log.out("Received query from " + packet.getAddress() + ":" + packet.getPort());
			} catch (IOException e) {
				e.printStackTrace();
			}
			
			//Check is packet is malformed.
			if(packet.getData()[packet.getData().length-1]!=0){
				Log.err("Query is malformed (terminated with non-null byte).");
			}
			
			String received = new String(packet.getData(), 0, packet.getLength()-1);
			Log.out("Query word: " + received);

			try {
				byte_str.write(received.getBytes());
				byte_str.write(0);
			} catch(IOException e) {
				System.out.println("Didn't write");
			}
			if(!my_word_list.isInList(received)) {
				
				SortedSet<String> word_list = my_word_list.getCloseWords(received);
				String words = "";
				try {
					byte_str.write(Integer.toString(word_list.size()).getBytes());
				} catch (IOException e) {
					System.out.println("Didn't write");
				}
				
				for(String s : word_list) {
					words = words + s + ", ";
					try {
						byte_str.write(s.getBytes(c));
						byte_str.write(0);
					} catch(IOException e) {
						System.out.println("Didn't write");
					}
				}
				words = words.substring(0, words.length() - 2);
				Log.out("Word is not spelled correctly, " + word_list.size() + " suggestions: " + words);
			} else {
				Log.out("Word is spelled correctly.");
				byte_str.write(0);
			}

			ret_bytes = byte_str.toByteArray();

			InetAddress address = packet.getAddress();
			int port = packet.getPort();
			packet = new DatagramPacket(ret_bytes, ret_bytes.length, address, port);

			try {
				my_socket.send(packet);
				Log.out("Sent query response to " + packet.getAddress() + ":" + packet.getPort());
			} catch (IOException e) {
				e.printStackTrace();
			}

		}
	}

}
